#include "Consultor.h"
#include "FuncionesAuxiliares.h"	// itos
#include "LockFile.h"
#include <sys/types.h> 	// getpid
#include <unistd.h>		// getpid
#include <iostream>		
#include <fstream>		// file
#include "SignalHandler.h"


using namespace std;

#define LOCKFILE "../include/Consultor.h"
#define BASEDATOS "../baseDatos.txt"
#define ACLIENTE "../Makefile"
#define DESERVIDOR "../make.sh"
#define LETRA 'a'

Consultor::Consultor() {
	SignalHandler* sh = SignalHandler :: getInstance();
	sh->registrarHandler(SIGINT, &sigint_handler);
	sigint_handler.bloquear();

	this->deServidor = new Cola<mensaje> ((char*) DESERVIDOR, LETRA);
	this->aCliente = new Cola<mensaje> ((char*) ACLIENTE, LETRA);
	nombre = itos((int) getpid());
}

Consultor::~Consultor() {
	SignalHandler :: destruir();

	delete this->deServidor;
	delete this->aCliente;
}	

void Consultor::recibirPeticion() {
	this->deServidor->leer ( 0, &(this->peticion) );
	//cout << "Lee el consultor: " << (this->peticion).texto << " - " << (this->peticion).tipo << endl;
}


void Consultor::resolverConsulta() {
	peticionDato = parsearPeticion(string(peticion.texto), peticion.tipo);
	//cout << "CONSULTOR: peticionDato: " << peticionDato << endl;
	generarResultado(this->peticionDato, this->nombre);
	string respuesta = leerResultado(this->nombre);
	//cout << "CONSULTOR: respuesta: " << respuesta << endl;
	if ( respuesta.empty() ) 
		responderPeticion("No se hallaron datos.");
	else
		responderPeticion(respuesta);
}


/* Arma el comando grep con los parametros correspondientes y lo ejecuta
 * Toma un lock de lectura.*/
void Consultor::generarResultado(string regexp, string archivoSalida) {
	string comando = string("grep '");
	comando += regexp + string("' ");
	comando += string(BASEDATOS);
	comando += string(" >> ");
	comando += archivoSalida;
	
	LockFile lock((char*) LOCKFILE, LECTURA);
	lock.tomarLock();
	
	system(comando.c_str());

	lock.liberarLock();
}


string Consultor::leerResultado(string nombreArchivo) {
	string linea, resultado;
	ifstream file (nombreArchivo);
	if ( ! file.is_open())
		return "";
	
	// Filtra en caso de que haya muchos resultados
	while ( file.good() ) {
		getline (file,linea);
		string aux = parsearPeticion(linea, (this->peticion).tipo);
		if ( ! aux.compare(this->peticionDato) ) 
			resultado = linea;
	}
	file.close();
	
	// Borra el archivo temporal generado
	string comando = string("rm ") + nombreArchivo;
	system( comando.c_str() );
	
	return resultado;
}


bool Consultor::altaRepetida() {
	vector<string> datos;
	datos.push_back( parsearPeticion(string(peticion.texto), PETICION_NOMBRE) );
	datos.push_back( parsearPeticion(string(peticion.texto), PETICION_DIRECCION) );
	datos.push_back( parsearPeticion(string(peticion.texto), PETICION_TELEFONO) );
	return tieneRepetido(datos);
}


bool Consultor::tieneRepetido(vector<string> datos) {
	string archivoTemporal = this->nombre + ".repe";
	for(unsigned int i = 0; i < datos.size(); i++) 
		generarResultado(datos[i], archivoTemporal);
	bool resultado = (! archivoVacio(archivoTemporal) );
	string comando = string("rm ") + archivoTemporal;
	system( comando.c_str() );
	return resultado;
}


bool Consultor::archivoVacio(string nombreArchivo) {
	string linea;
	bool vacio = true;
	ifstream file (nombreArchivo);
	if ( ! file.is_open())
		return false;
	
	while ( file.good() && vacio) {
		getline (file,linea);
		if ( ! linea.empty() )
			vacio = false;
	}
	file.close();
	
	return vacio;
}


void Consultor::responderPeticion(string respuesta) {
	peticion.tipo = RESPUESTA;
	strcpy(peticion.texto, respuesta.c_str() );
	peticion.mtype = peticion.remitente;
	
	this->aCliente->escribir(this->peticion );
}


bool Consultor::resolverAlta() {
	LockFile lock((char*) LOCKFILE, ESCRITURA);
	string datos = string(peticion.texto) + string("\n");
	lock.tomarLock();
	
	if ( altaRepetida() ) {
		lock.liberarLock();
		responderPeticion("REPETIDO");
		return false;
	}
	
	//lock.escribir((char*)datos.c_str(), datos.size());
	FILE* bd = fopen((char*) BASEDATOS, (const char*) "a+"); 
	if ( ! bd ) {
		lock.liberarLock();
		responderPeticion("ERROR");
		return false;
	}
	fseek( bd, 0, SEEK_END);
	fwrite((char*) datos.c_str(), sizeof(char), datos.size(), bd);
	fclose(bd);
	lock.liberarLock();
	responderPeticion("OK");
	return true;
}


bool Consultor::resolverModificacion() {
	// Primero obtiene los campos del pedido.
	string nombre = parsearPeticion(string(peticion.texto), PETICION_NOMBRE);
	string direccion = parsearPeticion(string(peticion.texto), PETICION_DIRECCION);
	string telefono = parsearPeticion(string(peticion.texto), PETICION_TELEFONO);

	// Si alguno de los campos está vacío, no se modifica.
	if(nombre == "")	
		return false;
		
	if(direccion == "")
		direccion = "\\1";
	
	if(telefono == "")
		telefono = "\\2";
		
	// Creo un archivo para guardar los cambios.
	string nombreArchivoTemporal = this->nombre + ".cambios";
		
	// Armo el comando sed a ejecutar.	
	//				                 \1        \2
	//                sed -i   _    nombre      & telviejo &dirvieja_
	string comando = "sed -i 's_" + nombre + "\\&\\(.*\\)\\&\\(.*\\)_";
	
	comando += nombre + "\\&" + direccion + "\\&" + telefono + "_";
	
	comando += "w " + nombreArchivoTemporal + "' " + BASEDATOS;
	
	//cout << comando << endl;
	
	// Tomo el lock y ejecuto el comando.
	LockFile lock((char*) LOCKFILE, ESCRITURA);
	lock.tomarLock();
	//sleep(20);
	system(comando.c_str());
	lock.liberarLock();

	// Si el archivo temporal está vacío, no se encontró el nombre.
	bool resultadoVacio = archivoVacio(nombreArchivoTemporal);
	
	// Borra el archivo temporal.
	comando = "rm " + nombreArchivoTemporal;
	system( comando.c_str() );
	
	return !resultadoVacio;

}


int Consultor::ejecutar() {
	cout << "CONSULTOR: espero petición." << endl;
	recibirPeticion();
	if (peticion.tipo == ALTA) {
		cout << "PETICIÓN TIPO: alta" << endl;
		resolverAlta();
	} 
	
	else if (peticion.tipo == MODIFICACION) {
		cout << "PETICIÓN TIPO: modif" << endl;
		if( resolverModificacion() ){
			responderPeticion("MODIFICION EXITOSA");
		}
		else
			responderPeticion("MODIFICACION INVALIDA");
	}
	
	else {
		cout << "PETICIÓN TIPO: consulta." << endl;
		resolverConsulta();
	}
	
	return 0;
}
